package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"reflect"
	"strings"

	"../ccpm/util/utillog"
	"../ccpm/util/utilsys"
	"./rtdebugger"

	"github.com/fsnotify/fsnotify"
)

// VERSION 版本号
const VERSION string = "0.0.1"

var hookRelDir = "./hook"

// HookFuncFMap 钩子函数对应关系
var HookFuncFMap = map[string]string{"write_file": "write_file"}

type wsConfig struct {
	ChildDirs  []string
	IsPathIter bool
	PathIgnore []string
}

var s wsConfig

var (
	watchdir string
)

func usage() {
	fmt.Fprintf(os.Stderr, `watch version: %s
	USAGE:watch [--tardir][..options] $tardir
	
	Options:
	`, VERSION)
}

func init() {
	flag.StringVar(&watchdir, "tardir", "", "where to watch :3")
	flag.Usage = usage
}

func lisWsHandler() {
	http.HandleFunc("/remote_debugger", rtdebugger.WsHandler)
	http.ListenAndServe("0.0.0.0:8089", nil)
}

func main() {
	flag.Parse()

	go lisWsHandler()
	if watchdir == "" {
		usage()
	} else {
		parseConfig()
		watchDir(watchdir)
	}
}

func hookFunc(hook string, args ...string) {
	pwd, _ := os.Getwd()
	if utilsys.IsProcessRunning(hook) {
		return
	}
	hookfile, ok := HookFuncFMap[hook]
	if !ok {
		log.Fatalf("hook file <%s> not exist！", hookfile)
	}
	hookhandler := path.Join(pwd, hookRelDir, hook)
	fmt.Println(hookhandler)
	_, err := os.Stat(hookhandler)
	ret := exec.Command(hookhandler, args...)

	out, err := ret.CombinedOutput()
	if err != nil {
		fmt.Println("execute hook failed!")
		fmt.Printf("[ERROR] %s", err)
		return
	}
	fmt.Printf("%s", out)
}

func parseConfig() {
	pwd, _ := os.Getwd()
	relpath, _ := filepath.Abs(path.Join(pwd, "config.json"))
	jstr, _ := ioutil.ReadFile(relpath)
	err := json.Unmarshal(jstr, &s)
	if err != nil {
		log.Fatalln(err)
		return
	}
	utillog.PrintStruct(reflect.TypeOf(s), reflect.ValueOf(s), 4)
}

func watchChildDirs(watcher *fsnotify.Watcher, pardir string, childdirs []string) {
	for _, dir := range childdirs {
		pathrel, _ := filepath.Abs(path.Join(pardir, dir))
		filepath.Walk(pathrel, func(path string, info os.FileInfo, err error) error {
			if info.IsDir() {
				path, err := filepath.Abs(path)
				if err != nil {
					return err
				}
				for _, pathstr := range s.PathIgnore {
					if strings.Count(path, pathstr) != 0 {
						return nil
					}
				}
				log.Println("监控文件夹 : ", path)
				err = watcher.Add(path)
			}
			return nil
		})
	}
}

func watchDir(dpath string) {
	pathrel, _ := filepath.Abs(dpath)
	fmt.Printf("\nWatching path %s\n...", pathrel)
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatalln(err)
	}
	defer watcher.Close()
	if err != nil {
		log.Fatalln(err)
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case ev := <-watcher.Events:
				{
					if ev.Op&fsnotify.Write == fsnotify.Write {
						log.Println("写入文件 : ", ev.Name)
						hookFunc("write_file", pathrel, ev.Name)
					}
				}
			case err := <-watcher.Errors:
				{
					log.Println("error : ", err)
					return
				}
			}
		}
	}()
	if s.IsPathIter {
		watchChildDirs(watcher, pathrel, s.ChildDirs)
	}
	err = watcher.Add(pathrel)
	if err != nil {
		log.Fatalln(err)
	}
	<-done
}
